import struct
from datetime import datetime
import numpy


def write_value(output ,value):
    """
    0: NONE
    1: int32
    2: flot64
    3: bool
    4: str
    5: list
    6: dict
    7: int16 : h
    8: int8 = > b
    9: int64
    10:datetime
    11: bytes， H类型（2个字节）长度
    12: bytes， I类型（4个字节）长度

    ###
    Int16, 等于short, 占2个字节. -32768 32767
    Int32, 等于int, 占4个字节. -2147483648 2147483647
    Int64, 等于long, 占8个字节. -9223372036854775808 9223372036854775807
    int8, -128 - 127
    ## 预留给barData的: [h_m_s, bar.volume, bar.open_price, bar.high_price, bar.low_price,bar.close_price, extra_dict]  51 * 240
    ## h_m_s: numpy.int16(h_m_s / 100)
    100：不包含extra： 二维数据结构[[listType,hms,volume,open,high,low,close]]  : 6个元素
          [时间:int16, volume平均值:int32, open平均值:int16:, high平均值:int16, low平均值int16, close平均值int16]
        listType:
        0:[byte8, 跟上面一样]
        1:[byte8, int16差值，跟上面一样]
        2:[byte8, int32差值，跟上面一样]
        3:[byte8, int16差值,    int8差值，              int8差值，        int8差值，          int8差值】  8 *
        4:[byte8, int16差值,    int16差值，              int16差值，        int16差值，          int16差值】 12 * 240
        5:[byte8, int32差值,    int8差值，              int8差值，        int8差值，          int8差值】
        6:[byte8, int32差值,    int16差值，              int16差值，        int16差值，          int16差值】


    """
    if value is None:
        output.write(struct.pack("B", 0))  ##写入类型
    elif isinstance(value, int) or isinstance(value,numpy.int64):
        if value <=127 and value >=-128:
            output.write(struct.pack("B", 8))  ##写入类型
            output.write(struct.pack("b", value))  ##写入值
        elif value <=32767 and value >=-32768:
            output.write(struct.pack("B", 7))  ## -32768 到 +32767之间
            output.write(struct.pack("h", value))
        elif value<=2147483647 and value >= -2147483648:
            output.write(struct.pack("B", 1))  ##写入类型
            output.write(struct.pack("i", value))  ##写入值
        else:
            output.write(struct.pack("B", 9) ) ##写入类型
            output.write(struct.pack("q", value))  ##写入值
    elif isinstance(value,numpy.int32):
        output.write(struct.pack("B", 1))  ##写入类型
        output.write(struct.pack("i", value))  ##写入值
    elif isinstance(value, float):
        output.write(struct.pack("B", 2))
        output.write(struct.pack("d", value))
    elif isinstance(value, bool) or isinstance(value, numpy.bool_):
        output.write(struct.pack("B", 3))
        output.write(struct.pack("?", value))
    elif isinstance(value, str):
        output.write(struct.pack("B", 4))
        value_bytes = value.encode('utf8')
        str_len = len(value_bytes)
        assert str_len < 65536
        output.write(struct.pack("H", str_len))
        output.write(value_bytes)
    elif isinstance(value, list):
        output.write(struct.pack("B", 5))
        list_size = len(value)
        output.write(struct.pack("H", list_size))
        for i in range(0, list_size):
            write_value(output, value[i])
    elif isinstance(value, dict):
        output.write(struct.pack("B", 6))
        write_dict(output ,value)
    elif isinstance(value, numpy.int16):
        output.write(struct.pack("B", 7))  ## -32768 到 +32767之间
        output.write(struct.pack("h", value))
    elif isinstance(value, datetime):
        output.write(struct.pack("B", 10))
        output.write(struct.pack("d", value.timestamp()))
    elif isinstance(value,bytes):
        byte_len = len(value)
        if byte_len > 65535:
            output.write(struct.pack("B", 12))
            output.write(struct.pack("I", byte_len))
        else:
            output.write(struct.pack("B", 11))
            output.write(struct.pack("H", byte_len))
        output.write(value)
    elif isinstance(value, tuple):
        raise RuntimeError(f"write_value unsupport type of tuple: {value} " )
    elif isinstance(value, set):
        raise RuntimeError(f"write_value unsupport type of set: {value}")
    else:
        raise RuntimeError(f"write_value unsupport type of : {value} " )


def read_value(buffer :bytes ,offset :int):
    """
    0: NONE
    1: int32
    2: flot64
    3: bool
    4: str
    5: list
    6: dict
    7: int16 : h
    8: int8 = > b
    9: int64
    10:datetime
    11: bytes， H类型（2个字节）长度
    12: bytes， I类型（4个字节）长度
    """
    [value_type] = struct.unpack_from("B" ,buffer, offset)
    offset += 1
    if value_type == 0:
        return None ,offset
    if value_type == 1:
        return int(struct.unpack_from("i" ,buffer ,offset)[0]) ,offset + 4
    if value_type == 2:
        return float(struct.unpack_from("d" ,buffer ,offset)[0]) ,offset + 8
    if value_type == 3:
        return struct.unpack_from("?" ,buffer ,offset)[0] ,offset + 1
    if value_type == 4:
        str_len = struct.unpack_from("H" ,buffer, offset)[0]
        offset +=2
        str_btes = buffer[offset:offset +str_len]
        return str(str_btes,"utf8") ,offset + str_len
    if value_type == 5:
        list_size = struct.unpack_from("H" ,buffer, offset)[0]
        offset += 2
        db_list = []
        for i in range(0 ,list_size):
            item_value ,offset = read_value(buffer ,offset)
            db_list.append(item_value)
        return db_list ,offset
    if value_type == 6:
        return read_dict(buffer ,offset)
    if value_type == 7:
        return int(numpy.int16(struct.unpack_from("h" ,buffer ,offset)[0])),offset + 2
    if value_type == 8:
        return int(numpy.int8(struct.unpack_from("b" ,buffer ,offset)[0])) ,offset + 1
    if value_type == 9:
        return int(struct.unpack_from("q" ,buffer ,offset)[0]) ,offset + 8
    if value_type == 10:
        _t =  float(struct.unpack_from("d" ,buffer ,offset)[0])
        return datetime.fromtimestamp(_t) ,offset + 8
    if value_type == 11:
        byte_len = struct.unpack_from("H" ,buffer ,offset)[0]
        offset +=2
        data_bytes = buffer[offset:offset + byte_len]
        return data_bytes, offset + byte_len
    if value_type == 12:
        byte_len = struct.unpack_from("I" ,buffer ,offset)[0]
        offset +=4
        data_bytes = buffer[offset:offset + byte_len]
        return data_bytes, offset + byte_len
    raise RuntimeError(f"read_value unsupport type of : {value_type}")



def write_dict(output ,_dict :{}):
    size = len(_dict)
    output.write(struct.pack("H" ,size))
    if size == 0:
        return output.getvalue()
    for name ,value in _dict.items():
        ##写入name值
        assert isinstance(name ,str) == True
        write_value(output ,name)
        write_value(output ,value)


def read_dict(buffer :bytes ,offset):
    dict_size = struct.unpack_from("H", buffer, offset)[0]
    offset+=2
    _dict = {}
    for i in range(0 ,dict_size):
        name ,offset= read_value(buffer ,offset)
        value ,offset = read_value(buffer ,offset)
        _dict[name] = value
    return _dict ,offset


